package com.pig4cloud.pigx.contract.executer.position.multiple_changer.impl;

import com.pig4cloud.pigx.common.customize.exception.MqAckException;
import com.pig4cloud.pigx.common.customize.i18n.util.MessageUtils;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.utils.CommonUtil;
import com.pig4cloud.pigx.contract.bo.ContractPositionMultipleChangeBO;
import com.pig4cloud.pigx.contract.constant.I18nMessages;
import com.pig4cloud.pigx.contract.dto.AppContractMultipleChangeDTO;
import com.pig4cloud.pigx.contract.dto.ContractHierarchyDTO;
import com.pig4cloud.pigx.contract.entity.ContractPosition;
import com.pig4cloud.pigx.contract.executer.position.multiple_changer.PositionMultipleChanger;
import com.pig4cloud.pigx.contract.mapper.ContractPositionMapper;
import com.pig4cloud.pigx.contract.mongo.entity.ContractSymbolMongo;
import com.pig4cloud.pigx.contract.utils.ContractUtils;
import com.pig4cloud.pigx.contract.vo.ContractHierarchyVO;
import io.seata.common.util.CollectionUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;

/**
 * 仓位杠杆调整器抽象实现
 *
 * @since 2022/8/17 11:17
 */
@Slf4j
@AllArgsConstructor
public abstract class AbstractPositionMultipleChanger implements PositionMultipleChanger {

    protected final ContractUtils contractUtils;
    protected final CommonUtil commonUtil;
    protected final MysqlUtil mysqlUtil;
    protected final ContractPositionMapper contractPositionMapper;

    /**
     * 获取仓位信息，并锁定
     *
     * @param bo bo
     * @since 2022/8/17 21:21
     */
    @Override
    public void getPosition(ContractPositionMultipleChangeBO bo) {
        AppContractMultipleChangeDTO dto = bo.getDto();
        if (Objects.isNull(dto.getMultiple())) {
            throw new RuntimeException("数据异常，multiple为空");
        }
        if (Objects.isNull(dto.getSymbolId())) {
            throw new RuntimeException("数据异常，symbolId为空");
        }
        if (Objects.isNull(dto.getUserId())) {
            throw new RuntimeException("数据异常，userId为空");
        }

        ContractPosition condition = ContractUtils.getCurrentPositionCondition(null,
                dto.getUserId(),
                dto.getSymbolId(),
                null,
                null);
        List<ContractPosition> list = contractUtils.getAllCurrentPosition(condition, true);

        if (CollectionUtils.isEmpty(list)) {
            throw new MqAckException();
        }

        // 遍历校验
        list.forEach(i -> ContractUtils.checkCurrentPosition(i, false));

        bo.setList(list);
        // 根据仓位信息初始化bo信息
        initBO(bo);
    }

    /**
     * 根据仓位信息初始化bo信息
     *
     * @param bo bo
     * @since 2022/9/8 11:44
     */
    private void initBO(ContractPositionMultipleChangeBO bo) {
        // 接下来使用到的仓位数据在同一币对下都是相同的，因此可以直接取首个仓位
        List<ContractPosition> list = bo.getList();
        ContractPosition position = list.get(0);
        bo.setFirstPosition(position);

        // 获取币对信息
        Long symbolId = position.getSymbolId();
        ContractSymbolMongo symbol = contractUtils.getSymbolById(symbolId);
        ContractUtils.checkSymbol(symbol);
        bo.setSymbol(symbol);
    }

    /**
     * 杠杆调整
     *
     * @param bo bo
     * @since 2022/8/17 11:15
     */
    @Override
    public void change(ContractPositionMultipleChangeBO bo) {
        // 校验仓位信息和新的杠杆倍数
        validChangeParam(bo);

        // JVM修改仓位杠杆倍数
        List<ContractPosition> list = bo.getList();
        AppContractMultipleChangeDTO dto = bo.getDto();
        list.forEach(i -> i.setMultiple(dto.getMultiple()));

        // 处理因杠杆倍数变化导致更改的其它相关数据
        handleRelevantData(bo);
        // 修改仓位信息
        list.forEach(i -> {
            if (contractPositionMapper.updateById(i) != 1) {
                throw new RuntimeException(MessageUtils.get(I18nMessages.FAILED_TO_MODIFY_POSITION_INFORMATION));
            }
        });
    }

    /**
     * 处理因杠杆倍数变化导致更改的其它相关数据
     *
     * @param bo bo
     * @since 2022/9/8 12:04
     */
    protected abstract void handleRelevantData(ContractPositionMultipleChangeBO bo);

    /**
     * 校验调整参数
     *
     * @param bo bo
     * @since 2022/9/8 11:23
     */
    protected void validChangeParam(ContractPositionMultipleChangeBO bo) {
        AppContractMultipleChangeDTO dto = bo.getDto();
        ContractPosition position = bo.getFirstPosition();

        if (dto.getMultiple() < 1) {
            throw new RuntimeException(MessageUtils.get(I18nMessages.MINIMUM_LEVERAGE_MULTIPLE_IS_ONE));
        }
        if (Objects.equals(dto.getMultiple(), position.getMultiple())) {
            throw new RuntimeException(MessageUtils.get(I18nMessages.LEVERAGE_MULTIPLES_DO_NOT_NEED_TO_BE_ADJUSTED));
        }

        // 根据新杠杆倍数获得头寸信息
        ContractHierarchyDTO condition = new ContractHierarchyDTO();
        condition.setMultiple(dto.getMultiple());
        condition.setSymbolId(dto.getSymbolId());
        ContractHierarchyVO hierarchy = contractUtils.getHierarchy(condition);

        // 校验总开仓数量
        double positionTotalNum = contractUtils.incrPositionTotalNum(position.getUserId(), position.getSymbolName(), BigDecimal.ZERO);
        ContractUtils.checkHierarchy(hierarchy, positionTotalNum, dto.getMultiple(), position.getSymbolName());
    }

    /**
     * 杠杆调整成功
     *
     * @param bo bo
     * @since 2022/8/17 11:31
     */
    @Override
    public void success(ContractPositionMultipleChangeBO bo) {
        // 提交本地事务
        mysqlUtil.commitLocalTx();
    }

    /**
     * 杠杆调整程序发生异常
     *
     * @param bo bo
     * @param e  异常
     * @since 2022/8/17 11:31
     */
    @Override
    public void fail(ContractPositionMultipleChangeBO bo, Exception e) {

    }
}
